#!/usr/bin/python
# -*- coding: UTF-8 -*-

# 任务报告云同步 3.2.0版本兼容

import rospy
import os, time, requests, json, yaml
from v3_msgs.msg import TaskGroup,TaskRecord,TaskPicture,TaskState
from v3_msgs.srv import *
from cln_msgs.msg import IpcState
from std_msgs.msg import String
from datetime import datetime
import copy

class HttpBridge:
    def __init__(self):
        self.__headers = {"Content-Type": "application/x-www-form-urlencoded"}
        self.__cloudplatform_url = rospy.get_param("cloudplatform_url", "cloud.aotbot.com/api")
        self.__qiniu_token = ""
        self.__qiniu_url = "http://up-z2.qiniup.com"
        self.__cache_file = "/home/aoting/user_config/cache_database/net_cache.yaml"

    def NetWorkConnected(self, ip, port):
        '''联网状态检测
        '''
        run_cmd = "echo quit | timeout --signal=9 2 telnet {} {}".format(ip, port)
        result = os.popen(run_cmd)
        data = result.read()
        # print(data)
        if "Connected" in data:
            return True
        return False

    def GetQiNiuToken(self):
        try:
            qiniu_url = "http://{}/qiniu/getUploadToken".format(self.__cloudplatform_url)
            client_data = {"isPrivate": "False"}
            ret = requests.post(url=qiniu_url,
                                data=client_data,
                                headers=self.__headers)
            login_data = json.loads(ret.text)
            print("[GetQiNiuToken] login_data: {}".format(login_data))
            if ret.status_code == 200:
                self.__qiniu_token = login_data["data"]
        except Exception as error:
            rospy.logwarn("[GetQiNiuToken]: %s", error)    
    
    def UploadFileToQiNqiu(self, group_id, file_path):
        '''上次文件到七牛云, 返回七牛云地址
        '''
        file_key = ""
        try:   
            self.GetQiNiuToken()
            if not self.__qiniu_token:
                raise Exception("qiniu_token is empty!")
            if not os.path.isfile(file_path):
                raise Exception("file_path doesn't exist!")
            upload_data = {
                "token": self.__qiniu_token,
                "key": "task_report/{}.png".format(group_id),
            }
            with open(file_path, 'rb') as file:
                files = {'file': ('image.png', file, 'image/png')}
                response = requests.post(url=self.__qiniu_url, data=upload_data, files=files)
                response_data = json.loads(response.text)
                if response.status_code == 200:
                    # 七牛云上传成功
                    file_key = response_data["key"]
                elif response.status_code == 614:
                    # 文件已存在
                    # raise Exception(" status_code is 614, file existed!")
                    file_key = upload_data["key"]
                # elif response.status_code == 401:
                #     raise Exception("token error")
                else:
                    raise Exception(" status_code is ".format(response.status_code))
        except Exception as error:
            rospy.logwarn("[UploadFileToQiNqiu] error: %s", error)   
        return file_key

    def TaskReportUpload2Cloud(self, task_report):
        '''任务报告上传
        '''
        try:
            # print("TaskReportUpload2Cloud......,task_report: ",task_report)
            url = "http://{}/taskReport/insertTaskReport".format(self.__cloudplatform_url)
            res = requests.post(url, headers=self.__headers, data=task_report)
            # print("[TaskReportUpload2Cloud]", res.text)
            # print("[TaskReportUpload2Cloud]", res.status_code)
            response_data = json.loads(res.text)
            if res.status_code == 200:  # 上传成功
                if "message" in response_data:
                    if response_data["message"]["code"] == 0:
                        return True
                    else:
                        # print(response_data["message"]["message"])
                        raise Exception(response_data["message"]["message"].decode('unicode_escape'))
            else:
                raise Exception (res.text.encode("utf-8"))
        except Exception as error:
            rospy.logwarn("[TaskReportUpload2Cloud] error: %s", error)  
        return False 

class TaskReportUpload:
    def __init__(self):
        rospy.init_node("task_report_upload_node")
        self.__http_bridge = HttpBridge()
        self.__robot_sn = ""
        self.__cloudplatform_token = ""
        rospy.Subscriber("/cloudplatform_token", String, callback=self.TokenCallback)
        rospy.Subscriber("/ipc_state", IpcState, callback=self.IpcStateCallback)
        rospy.Subscriber("/upload_taskgroup_id", String, callback=self.GroupIdCallback, queue_size=100)
        self.__task_report_client = rospy.ServiceProxy("/taskgroup_record_server", TaskReportManager)
        self.__db_dir = "/home/aoting/user_config/db"
        if not os.path.exists(self.__db_dir):
            os.makedirs(self.__db_dir)
        self.__upload_cache_file = os.path.join(self.__db_dir, "task_report_upload.json")
        self.__upload_record_data = list()
        self.UploadThread()
    
    def ReadJsonFile(self, file_path):
        '''Json 数据读取
        '''
        try:
            if not os.path.exists(file_path):
                raise Exception("file does't existed!")
            with open(file_path, 'r') as file:
                data = json.load(file)
                return data
        except Exception as error:
            rospy.logwarn("[ReadJsonFile] error: %s", error)
        return list()
    
    def WriteJsonFile(self, file_path, data):
        '''Json 数据写入
        '''
        try:
            # 读取数据
            file_data = self.ReadJsonFile(file_path)
            data_idx = -1
            for i in range(len(file_data)):
                record_item = file_data[i]
                if "group_id" in record_item and record_item["group_id"] == data["group_id"]:
                    data_idx = i
                    break
            if data_idx < 0:
                file_data.append(data)
            else:
                file_data[data_idx] = data
            with open(file_path, 'w') as file:
                json.dump(file_data, file, indent=4)      # `indent` 参数使 JSON 格式化为更可读的形式
        except Exception as error:
            rospy.logwarn("[WriteJsonFile] error: %s", error)

    def TokenCallback(self, msg):
        '''云平台token
        '''
        self.__cloudplatform_token = msg.data
    
    def IpcStateCallback(self, msg):
        '''工控机状态
        '''
        self.__robot_sn = msg.robot_sn
    
    def GetNowTimeStr(self):
        '''获取当前时间字符串
        '''
        now = datetime.now()
        timestamp_str = now.strftime('%Y-%m-%d %H:%M:%S')
        return timestamp_str

    def CleanOldRecord(self, file_path, days):
        '''清理days天之前的上传数据
        '''
        try:
            now = datetime.now()
            # 读取数据
            file_data = self.ReadJsonFile(file_path)
            clean_idx = -1
            for i in range(len(file_data)):
                record_item = file_data[i]
                if "save_time" in record_item and record_item["save_time"]:
                    format_str = "%Y-%m-%d %H:%M:%S"  # 根据你的时间戳格式调整
                    dt = datetime.strptime(record_item["save_time"], format_str)
                    if(now - dt).days > 15:
                        clean_idx = i
                        break
            if clean_idx < 0: return
            del file_data[:clean_idx]
            with open(file_path, 'w') as file:
                json.dump(file_data, file, indent=4)      # `indent` 参数使 JSON 格式化为更可读的形式
        except Exception as error:
            rospy.logwarn("[CleanOldRecord] error: %s", error)
    
    def GroupIdCallback(self, msg):
        '''ID数据
        '''      
        new_group_id = msg.data
        # 检测是否已存在
        exist = False
        for record_item in self.__upload_record_data:
            if "group_id" in record_item and record_item["group_id"] == new_group_id:
                exist = True
                break
        if exist:
            rospy.logwarn("[GroupIdCallback] {} already exists.".format(new_group_id))
        else:
            upload_data = {
                "group_id": new_group_id,
                "group_name": "",
                "upload_state": False,
                "upload_time": "",
                "save_time": self.GetNowTimeStr(),
                "qiniu_key": "",
                "error_msg": ""
            }
            self.WriteJsonFile(self.__upload_cache_file, upload_data)
            self.__upload_record_data.append(upload_data)
    
    def QueryTaskRecords(self, group_id):
        '''查询任务报告数据
        '''
        task_records = list()
        try:
            client_data = TaskReportManagerRequest()
            client_data.option = client_data.QUERY_TASK_RECORD
            client_data.task_group.group_id = group_id
            res_data = self.__task_report_client.call(client_data)
            if not res_data.response.success:
                raise Exception(res_data.response.message)
            task_records = res_data.task_records
        except Exception as error:
            rospy.logwarn("[QueryTaskRecords] error: %s", error)  
        finally:
            return task_records 

    def TaskReportPacked(self, record_data):
        '''任务报告数据封装
        '''
        # data = TaskRecord()
        json_data = dict()
        json_data["id"] = record_data.task_group.group_id
        json_data["startTime"] = record_data.start_time
        json_data["endTime"] = record_data.end_time
        json_data["taskName"] = record_data.task_group.group_name
        json_data["mapName"] = ""
        if record_data.task_group.task_list:
            json_data["mapName"] = record_data.task_group.task_list[0].task_base.map_base.name
        # json_data["coverage"] = "{:.2f}".format(record_data.coverage_rate * 100)
        json_data["coverage"] = "{:.4f}".format(record_data.coverage_rate * 100)
        json_data["durationTime"] = record_data.working_time
        json_data["area"] = "{:.4f}".format(record_data.coverage_rate * record_data.total_area)
        # 进度
        # json_data["progress"] = "{:.2f}".format(record_data.coverage_rate * 100)
        # 失败原因
        json_data["finishReason"] = record_data.error_code.error_msg
        # 七牛云图片地址 暂只显示一个图片
        json_data["mapUrl"] = ""
        if record_data.task_pictures:
            task_picture = record_data.task_pictures[0]
            if task_picture.png_file_path:
                json_data["mapUrl"] = task_picture.qiniu_file_key
        json_data["sendNotify"] = rospy.get_param("/warn_state/alltask_finished",default="1")
        json_data["token"] = self.__cloudplatform_token
        json_data["origin"] = "[]"
        json_data["pre"] = "[]"
        json_data["startPoint"] = "[]"
        json_data["deviceSerialNum"] = self.__robot_sn
        # json_data["deviceSerialNum"] = "A010FFFF119669B4A3C2819"
        return json_data

    def UploadThread(self):
        os.system("ping -c 1 up-z2.qiniup.com")
        self.CleanOldRecord(self.__upload_cache_file, 15)     #清理15天之前的旧数据
        self.__upload_record_data = self.ReadJsonFile(self.__upload_cache_file)
        while not rospy.is_shutdown():
            time.sleep(5)   #5秒检测一次

            print("[UploadThread] size: {}".format(len(self.__upload_record_data)))
            if not self.__upload_record_data:
                continue

            # 获取待上传任务
            error_msg = ""
            for i in range(len(self.__upload_record_data)):
                if rospy.is_shutdown(): break
                record_item = self.__upload_record_data[i]
                # 已上传直接跳过
                if record_item["upload_state"]:
                    continue
                # 查询任务数据
                group_id = record_item["group_id"]
                # 只尝试3次上传
                for n in range(3):
                    try:
                        task_records = self.QueryTaskRecords(group_id)
                        if not task_records: 
                            raise Exception("report query failed!")
                        task_record = task_records[0]
                        if task_record.status.status == TaskState.USER_CANCEL and task_record.coverage_rate < 0.1:
                            # 任务取消且进度小于0.1的不上传
                            break
                        # print(task_record)
                        # 准备上传到七牛云
                        qiniu_file_key = ""
                        if not record_item["qiniu_key"]:
                            if task_record.task_pictures:
                                task_picture = task_record.task_pictures[0]
                                if not task_picture.png_file_path:
                                    raise Exception("get png_file_path failed!")
                                else:
                                    qiniu_conncet_state = self.__http_bridge.NetWorkConnected("up-z2.qiniup.com", 80)
                                    if not qiniu_conncet_state:
                                        raise Exception("qiniu_conncet_state is false!")
                                    qiniu_file_key = self.__http_bridge.UploadFileToQiNqiu(group_id, task_picture.png_file_path)
                        else:
                            qiniu_file_key = record_item["qiniu_key"]
                        if not qiniu_file_key:
                            raise Exception("get qiniu_file_key failed!")
                        print("[UploadThread] qiniu_file_key: {}".format(qiniu_file_key))
                        task_record.task_pictures[0].qiniu_file_key = qiniu_file_key
                        # 准备上传云平台
                        cloud_conncet_state = self.__http_bridge.NetWorkConnected("cloud.aotbot.com", 10500)
                        if not cloud_conncet_state:
                            raise Exception("cloud_conncet_state is false!")
                        upload_data = self.TaskReportPacked(task_record)
                        cloud_upload_state = self.__http_bridge.TaskReportUpload2Cloud(upload_data)
                        print("[UploadThread] cloud_upload_state: {}".format(cloud_upload_state))
                        if cloud_upload_state:
                            up_record_item = copy.deepcopy(record_item)
                            up_record_item["qiniu_key"] = qiniu_file_key
                            up_record_item["upload_state"] = True
                            up_record_item["group_name"] = task_record.task_group.group_name
                            up_record_item["upload_time"] = self.GetNowTimeStr()
                            self.WriteJsonFile(self.__upload_cache_file, up_record_item)
                            self.__upload_record_data[i] = up_record_item
                        break
                    except Exception as error:
                        # rospy.logwarn("[UploadThread] group_id: {}, count: {} error: {}".format(group_id, n+1, error)) 
                        record_item["error_msg"] = error
                        time.sleep(1)
                        # self.WriteJsonFile(self.__upload_cache_file, self.__upload_record_data)

if __name__ == "__main__":
    TaskReportUpload()
    rospy.spin()